package cn.edu.xmu.oomall.comment.dao;

import cn.edu.xmu.javaee.core.exception.BusinessException;
import cn.edu.xmu.javaee.core.mapper.RedisUtil;
import cn.edu.xmu.javaee.core.model.ReturnNo;
import cn.edu.xmu.oomall.comment.dao.bo.Comment;
import cn.edu.xmu.oomall.comment.mapper.CommentPoMapper;
import cn.edu.xmu.oomall.comment.mapper.po.CommentPo;
import lombok.RequiredArgsConstructor;
import cn.edu.xmu.javaee.core.util.CloneFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

@Repository
@RequiredArgsConstructor
public class CommentDao {
    private final static Logger logger = LoggerFactory.getLogger(CommentDao.class);

    private final CommentPoMapper commentPoMapper;
    private final RedisUtil redisUtil;

    private static final String KEY = "C%d";
    private static final String COMMENT_PRODUCT_KEY = "CP%d";
    private static final String COMMENT_ORDER_ITEM_KEY = "COI%d";
    private static final String COMMENT_STATUS_KEY = "CS%d";

    @Value("${oomall.comment.timeout}")
    private int timeout;

    private void build(Comment bo){
        bo.setCommentDao(this);
    }


    private Comment build(CommentPo po, Optional<String> redisKey) throws RuntimeException{
        Comment bo = CloneFactory.copy(new Comment(), po);
        this.build(bo);
        redisUtil.set(redisKey.get(), bo, timeout);
        return bo;
    }

    /**
     * 保存评论
     * @param bo 评论
     * @return 评论
     */
    public Comment insert(Comment bo) {
        String key = String.format(KEY, bo.getId());
        CommentPo po = CloneFactory.copy(new CommentPo(), bo);
        logger.debug("save:{}",po);
        CommentPo commentPo = commentPoMapper.save(po);
        return build(commentPo, Optional.of(key));
    }

    /**
     * 通过id查找评论
     * @param id 评论id
     * @return 评论
     */
    public Comment findById(Long id) {
        String key = String.format(KEY, id);
        CommentPo po = (CommentPo) redisUtil.get(key);

        if(!Objects.isNull(po)){
            return build(po, Optional.of(key));
        }

        Optional<CommentPo> commentPo = commentPoMapper.findById(id);
        if(commentPo.isPresent()){
            return build(commentPo.get(), Optional.of(key));
        }else{
            throw new BusinessException(ReturnNo.RESOURCE_ID_NOTEXIST, String.format(ReturnNo.RESOURCE_ID_NOTEXIST.getMessage(),"评论", id));
        }
    }

    /**
     * 通过商品id查找评论
     * @param id 商品id
     * @return 评论列表
     */
    public List<Comment> retrieveByProductId(Long id, Integer page, Integer pageSize) {
        String key = String.format(COMMENT_PRODUCT_KEY, id);
        List<Long> commentIds = (List<Long>) redisUtil.get(key);

        if(!Objects.isNull(commentIds)){
            return commentIds.stream().map(this::findById).toList();
        }

        Pageable pageable = PageRequest.of(page - 1, pageSize);
        Page<CommentPo> ret = commentPoMapper.findByProductIdAndStatusEquals(id, Comment.ACCEPT, pageable);
        if(ret.isEmpty()){
            return new ArrayList<>();
        }else{
            List<Comment> commentList = ret.stream().map(po -> {
                String commentKey = String.format(KEY, po.getId());
                return build(po, Optional.of(commentKey));
            }).toList();

            redisUtil.set(key, (ArrayList<Long>)commentList.stream().map(Comment::getId).collect(Collectors.toList()), timeout);
            return commentList;
        }
    }

    /**
     * 更新评论
     * @param comment 评论
     */
    public Comment save(Comment comment) {
        String key = String.format(KEY, comment.getId());
        CommentPo po = commentPoMapper.save(CloneFactory.copy(new CommentPo(), comment));
        if(Objects.isNull(po)){
            throw new BusinessException(ReturnNo.RESOURCE_ID_NOTEXIST, String.format(ReturnNo.RESOURCE_ID_NOTEXIST.getMessage(),"评论", comment.getId()));
        }
        redisUtil.del(key);

        return build(po, Optional.of(key));
    }

    /**
     * 通过订单明细id查找评论
     * @param oid 订单明细id
     * @return 评论
     */
    public Comment retrieveByOrderItemId(Long oid) {
        String key = String.format(COMMENT_ORDER_ITEM_KEY, oid);
        Comment comment = (Comment) redisUtil.get(key);
        if(Objects.nonNull(comment)){
            return comment;
        }
        Optional<CommentPo> commentPo = commentPoMapper.findByOrderItemId(oid);
        if(commentPo.isPresent()){
            return build(commentPo.get(), Optional.of(key));
        }else {
            return null;
        }
    }

    /**
     * 通过状态查找评论
     * @param status 状态
     * @return 评论列表
     */
    public List<Comment> retrieveByStatus(Byte status, Integer page, Integer pageSize) {
        String key = String.format(COMMENT_STATUS_KEY, status);
        Pageable pageable = PageRequest.of(page - 1, pageSize);
        List<Long> commentIds = (List<Long>) redisUtil.get(key);
        if(!Objects.isNull(commentIds)){
            return commentIds.stream().map(this::findById).collect(Collectors.toList());
        }
        Page<CommentPo> commentPoPage = commentPoMapper.findByStatus(status, pageable);
        if(commentPoPage.isEmpty()) {
            return new ArrayList<>();
        }else {
            List<Comment> commentList = commentPoPage.stream().map(po -> {
                String commentKey = String.format(KEY, po.getId());
                return build(po, Optional.of(commentKey));
            }).toList();
            redisUtil.set(key, (ArrayList<Long>)commentList.stream().map(Comment::getId).collect(Collectors.toList()), timeout);
            return commentList;
        }
    }

    /**
     * 通过pid查找评论
     * @param pid 评论id
     */
    public Comment retrieveByPid(Long pid) {
        Optional<CommentPo> commentPo = commentPoMapper.findByPid(pid);
        if(commentPo.isPresent()){
            return CloneFactory.copy(new Comment(), commentPo.get());
        }else{
            return null;
        }
    }
}
